package com.redxiii.spike.util.message;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.configuration.HierarchicalConfiguration;
import org.apache.commons.configuration.SubnodeConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.redxiii.spike.api.command.Command;
import com.redxiii.spike.api.command.MsgInterpreter;
import com.redxiii.spike.api.message.Message;

/**
 * @author Daniel Filgueiras
 * 08/05/2011
 */
public class BasicMsgInterpreter implements MsgInterpreter {

	private static final Logger logger = LoggerFactory.getLogger(BasicMsgInterpreter.class);
	private Map<String,CommandConfiguration> commands = new HashMap<String,CommandConfiguration>();
	private Map<String,CommandConfiguration> regexps = new HashMap<String,CommandConfiguration>();
	
	@SuppressWarnings("unchecked")
	@Override
	public void add(Class<? extends Command> cmdClass, HierarchicalConfiguration config) {
		
		if (commands.containsKey(cmdClass.getCanonicalName())) {
			logger.warn("Command duplicated: {}", cmdClass.getCanonicalName());
			return;
		}
		
		CommandConfiguration map = new CommandConfiguration();
		map.commandClass = cmdClass;
		map.config = config;
		commands.put(cmdClass.getCanonicalName(), map);
		
		List<SubnodeConfiguration> expressions = config.configurationsAt("expressions.regexp");
		for (SubnodeConfiguration cmdExp : expressions){
			String regexp = (String) cmdExp.getProperty("[@value]");
			if (regexps.containsKey(regexp)) {
				logger.warn("Duplicated regexp. [[ {} ]] already defined for command {}", regexp, regexps.get(regexp).commandClass);
			} else {
				
				try {
					Pattern.compile(regexp);
					regexps.put(regexp, map);
				} catch (Exception e) {
					logger.warn("Invalid regexp: {}", regexp, e);
					continue;
				}
				
				List<SubnodeConfiguration> paramNodes = cmdExp.configurationsAt("parameter");
				
				for (SubnodeConfiguration paramNode : paramNodes){
					Map<Integer,String> parans = map.parameters.get(regexp);
					if (parans == null) {
						parans = new HashMap<Integer,String>();
						map.parameters.put(regexp, parans);
					}
					parans.put(paramNode.getInt("[@id]"),(String)paramNode.getProperty("[@value]"));
					
				}
			}
		}
	}
	

	@Override
	public Command parseMessage(Message message) {
		
		for (Entry<String, CommandConfiguration> entry : regexps.entrySet()) {
			Pattern pattern = Pattern.compile(entry.getKey());
			Matcher matcher = pattern.matcher(message.toText());
			if (!matcher.find()) {
				continue;
			}
			
			Command command = null;

			try {
				command = entry.getValue().commandClass.newInstance();
			} catch (Exception e) {
				logger.error("Unable to create Command object for class: {}",entry.getValue().commandClass,e);
				return null;
			}
			
			
			Map<Integer,String> param = entry.getValue().parameters.get(entry.getKey());
			for (int c = 1; c <= matcher.groupCount(); c++) {
				String fieldName = param.get(c);
				String value = matcher.group(c);
				try {
					Field field = entry.getValue().commandClass.getDeclaredField(fieldName);
					field.setAccessible(true);
					field.set(command, value);
				} catch (Exception e) {
					logger.error("Error setting field {} ", fieldName + "/" + c, e);
				}
			}
			
			
			return command;
		}
		
		return null;
	}
}

class CommandConfiguration {
	Class<? extends Command> commandClass;
	
	// <regexp,   <id, fieldName >> 
	Map<String,Map<Integer,String>> parameters = new HashMap<String,Map<Integer,String>>();
	
	Map<Integer, String> paramDefaultVal = new HashMap<Integer, String>();
	
	HierarchicalConfiguration config;
}
